/**
 * Trabajo que consiste en calcular el numero de palabras existentes o palabras repetidas
 * en los archivos de texto almacenados hasta el momento, dependiendo de la opcion ingresada
 * por el usuario.
 * 
 * @author HPCIII_Team2013
 */
package cl.ucn.hpc.wordCounter;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;

public class WordCounterWork implements IRunnableTaskWordCounter {
	private String filepath;
	private String URL;
	private int opcion;
	private int inicio;
	private int fin;
	private LinkedBlockingQueue <Integer> finishedWorks;
	private LinkedBlockingQueue <String> resultado;
	private LinkedBlockingQueue <String> resultadoTemp;

	/**
	 * Constructor de la clase.
	 * 
	 * @param path Directorio general donde se encuentran los archivos a procesar.
	 * @param opcion La opcion ingresada por el usuario.
	 * @param URL Link que se debe procesar, si existe. De otro modo es "".
	 * @param inicio El correlativo de la pagina en la que debe iniciar el conteo. -1 
	 * si es solo una pagina (opcion 2 del menu)
	 * @param fin El correlativo de la pagina en la que debe finalizar el conteo. -1 
	 * si es solo una pagina (opcion 2 del menu)
	 * @param resultado Lista bloqueante en la cual se almacena el resultado
	 * @param finishedWorks Lista Bloqueante en la cual se suma el trabajo una vez terminado
	 */
	public WordCounterWork(final String path, int opcion, String URL, int inicio, int fin, LinkedBlockingQueue<String> resultado, LinkedBlockingQueue<Integer> finishedWorks){
		this.URL = URL;
		this.opcion = opcion;
		this.filepath = path;
		this.inicio = inicio;
		this.fin = fin;
		this.resultado = resultado;
		this.finishedWorks = finishedWorks;
		
	}

	/**
     * Realiza el conteo de palabras.
     *
	 * @see cl.ucn.hpc.webCrawler.IRunnableTask#execute()
	 */
	@Override
	public void execute() {
		Integer suma =0;
		
		if(opcion == 1){
			try {
				globalWordCounter();
				
			} catch (InterruptedException e) {
				e.printStackTrace();
			} 
		}else if(opcion ==2){
			try {			
				repeatedWordCounter(-1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}else{			
			try {
				globalRepeatedWordCounter();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		try {
			for(Integer FW : this.finishedWorks){
				suma = suma + FW;
			}
			this.finishedWorks.clear();
			this.finishedWorks.put((suma +1));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Cuenta la cantidad de palabras dentro del texto de la pagina entregada en parametro.
	 * 
	 * @param correlativo Correlativo de la pagina en el documento 0pagsRegis.txt. Si es -1, el numero es desconocido 
	 * y se debe usar el metodo findCorrelativo para encontrarlo.
	 * @throws InterruptedException Si se interrumpe la espera de put al esperar que exista capacidad en la cola para
	 * insertar su elemento.
	 */
	public void wordCounter(int correlativo) throws InterruptedException{		
		File file;
		long words = 0;
		if(correlativo == -1){
			file = new File(filepath+File.separatorChar +findCorrelativo()+"textoPag.txt");
		}else{
			file = new File(filepath+File.separatorChar +correlativo+"textoPag.txt");
		}
		
		try{
			Scanner in = new Scanner(file);
			
			while(in.hasNext()){
	            in.next();
	            words++;
	        }
			
			in.close();	
		}catch(IOException e){
		}	
		this.resultado.put(""+words);
	}
	
	/**
	 * Retorna la linea en la que se encuentra la URL especificada en el atributo de WordCounterWork (URL).
	 * 
	 * @return numero de la linea.
	 */
	protected int findCorrelativo() {	
	    
	    try {
	    	BufferedReader buf = new BufferedReader(new InputStreamReader(new DataInputStream(new FileInputStream(filepath+ File.separatorChar +"0pagsRegist.txt"))));
		    String line;
		    int lineNumber = 0;
			while ((line = buf.readLine()) != null){
			    lineNumber++;
			    if (this.URL.equals(line)){
			    	buf.close();
			        return lineNumber;
			    }
			}
			buf.close();
		} catch (IOException e) {
			return -1;
		}
	    	    
	    return -1;
	}
	
	/**
	 * Cuenta la cantidad de palabras en el texto de las paginas almacenadas actualmente.
	 * 
	 * @throws InterruptedException Si se interrumpe la espera de put al esperar que exista capacidad en la cola para
	 * insertar su elemento.
	 */
	public void globalWordCounter() throws InterruptedException{
		long words = 0;
		for (int i=this.inicio; i <=this.fin; i++){
			wordCounter(i);
			words = words + Long.parseLong(this.resultado.take());
		}
		this.resultado.put("" + words);
	}
	
	/**
	 * Retorna cuantas veces se repite cada palabra en el texto de la pagina web especificada 
	 * por parametros.
	 * 
	 * @param correlativo Correlativo de la pagina en el documento 0pagsRegis.txt. Si es -1, el numero es desconocido 
	 * y se debe usar el metodo findCorrelativo para encontrarlo.
	 * @throws InterruptedException Si se interrumpe la espera de put al esperar que exista capacidad en la cola para
	 * insertar su elemento.
	 */
	public void repeatedWordCounter(int correlativo) throws InterruptedException{		
		File file;
		List<String> allWords = new ArrayList<String>();
		Set<String> words = new HashSet<String>();
		List<String>pendingWords = null;
		int contadorPalabra=0;
		List<String> result = new ArrayList<String>();
		
		if(correlativo == -1){
			file = new File(filepath+File.separatorChar +findCorrelativo()+"textoPag.txt");
		}
		else{
			file = new File(filepath+File.separatorChar +correlativo+"textoPag.txt");
		}
		try{	
			Scanner in = new Scanner(file);
			while(in.hasNext()){
				allWords.add(in.next().trim().toLowerCase().replace(String.valueOf((char) 65279), ""));
			}
					
			if(allWords != null){
				for(String word : allWords){
					words.add(word);
				}
			}	
				
			pendingWords = new ArrayList<String>(words);
			words.clear();
		
			while(pendingWords.isEmpty() == false){
				contadorPalabra =0;
				String w = pendingWords.get(0);
				for(String word : allWords){
					if (word.equalsIgnoreCase(w)){
						contadorPalabra++;
					}
				}
			
				result.add(w + " " + contadorPalabra);
				pendingWords.remove(0);
			}
		
			for(String word : result){
				if(this.opcion ==2){
					this.resultado.put("" + word);
				}else{
					this.resultadoTemp.put(""+word);
				}
			}
		
			in.close();
		}
		catch(IOException e){
			if(opcion ==2){
				this.resultado.put("esta pagina no existe");
			}
		}
	}
	
	/**
	 * Calcula cuantas veces se repite cada palabra en el texto de las paginas almacenadas actualmente.
	 * 
	 * @throws InterruptedException Si se interrumpe la espera de put al esperar que exista capacidad en la cola para
	 * insertar su elemento.
	 */
	public void globalRepeatedWordCounter() throws InterruptedException{			
		List<String> tempWords = new ArrayList<String>();
		String [] tempWordsSplitted = null;
		String [] resultSplitted = null;
		String tempWord = "";
		long suma = 0;
		this.resultadoTemp = new LinkedBlockingQueue <String>();
		
		for(int i =this.inicio; i<=this.fin;i++){
			repeatedWordCounter(i);
			
		}
		for(String word: this.resultadoTemp){			
			resultSplitted = word.split(" ");
			suma = Long.parseLong(resultSplitted[1]);
			
			for(int i = 0; i<tempWords.size();i++){
				tempWordsSplitted = tempWords.get(i).split(" ");				
			
				if(resultSplitted[0].equals(tempWordsSplitted[0])){
					suma = suma + Long.parseLong(tempWordsSplitted[1]);
					tempWords.remove(i);
				}
			}
			
			tempWord = resultSplitted[0] + " "+ suma;	
			tempWords.add(tempWord);
		}
		
		this.resultadoTemp.clear();
		
		for(String word: tempWords){
			this.resultado.add(word);
		}
	}
}


